#include <iostream>
#include <sys/types.h> 
#include <sys/socket.h>
#include "log.hpp"
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
Log lg;

using func_t = std::function<std::string(const std::string&)>;


class udpserver
{
public:
    udpserver(uint16_t port=8080, string ip="0.0.0.0")
        : _port(port), _ip(ip)
    {
    }

    void Start()
    {
        // 1.创建套接字
        // 第一个参数：创建套接字的类型
        // 第二个：创建套接字时所需的服务类型（字节流、数据报）
        // 第三个：创建套接字的协议类别（一般前两个就可以确定了，可以不写）
        _socketfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_socketfd < 0)
        {
            lg(Fatal, "error socket: %s, error code: %d", strerror(errno), errno);
            exit(-1);
        }
        lg(Info, "socket success: %s, error code: %d", strerror(errno), errno);

        // 网络套接字
        struct sockaddr_in local;
        // 初始化结构
        memset(&local, 0, sizeof(struct sockaddr_in));
        local.sin_family = AF_INET;    // 类型
        local.sin_port = htons(_port); // 端口号要转为网络字节序
        // 如果ip地址为空，最好选择宏INADDR_ANY,表示可以绑定任意ip
        //local.sin_addr.s_addr =inet_addr(_ip.c_str()); // ip地址 因为一般的串口号输入是字符串 "" ，我们得转换为四字节整数ip
        //网络地址为INADDR_ANY,因为服务器可能有多个网卡,每个网卡也可能绑定多个IP 地址, 
        //这样设置可以在所有的IP地址上监听,直到与某个客户端建立了连接时才确定下来到底用哪个IP 地址;
        local.sin_addr.s_addr = INADDR_ANY;
        // 2.绑定端口号 强转为const struct sockaddr*，函数中识别结构体前16位来确定是网络套接字
        int n = bind(_socketfd, (const struct sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            lg(Fatal, "error bind: %s, error code: %d", strerror(errno), errno);
            exit(-2);
        }
        lg(Info, "bind success: %s, error code: %d", strerror(errno), errno);
    }

    void Run(func_t func)
    {
        // 服务器一直跑，有消息来就接收消息
        char buffer[1024]; // 缓冲区储存发来的信息
        // 后面两个参数是对端网络相关的属性信息，包括协议家族、IP地址、端口号等 以及结构体长度
        struct sockaddr_in recv;
        socklen_t len = sizeof(recv);

        while (1)
        {
            ssize_t n = recvfrom(_socketfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&recv, (socklen_t *)&len);
            if (n < 0)
            {
                lg(Warning, "error recvfrom: %s, error code: %d", strerror(errno), errno);
                continue;
            }
            //lg(Info, "recvfrom success: %s, error code: %d", strerror(errno), errno);
            

            //服务器收到消息然后将信息发送给对应的主机
            //后两个参数是对应主机的信息
            if(n>0)
            {
                //可以通过回调函数来处理这个信息
                string s=func(buffer);
                sendto(_socketfd,s.c_str(),s.size(),0,(struct sockaddr *)&recv,len);
                cout<<s<<endl;
                memset(buffer,0,sizeof(buffer));
                
            }
        }
    }

private:
    int _socketfd;
    uint16_t _port;
    std::string _ip;
};